package com.linksky.crawler;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.linksky.entity.Comment;
import com.linksky.entity.PlayList;
import com.linksky.entity.Reply;
import com.linksky.entity.Song;
import com.linksky.entity.User;
import com.google.common.collect.ImmutableMap;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import sun.misc.BASE64Encoder;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class Crawler {

    /*生成post请求所需参数*/
    private static final String ENCSECKEY = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c";
    private static String FIRST_PARAM = "{rid:\"\", offset:\"offsetNum\", total:\"true\", limit:\"20\", csrf_token:\"\"}";
    private static final String FIRTH_PARAM = "0CoJUm6Qyw8W8jud";
    private static final String AES_IV = "0102030405060708";

    /*待爬取的歌单队列*/
    private List<PlayList> newPlayLists = new ArrayList<>();
    private boolean loading = false;

    /*向list中添加待爬取的数据*/
    public synchronized void addNewPlayList(List<PlayList> newPlayLists) {
        while (loading) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.newPlayLists.addAll(newPlayLists);
        loading = true;
        notifyAll();
    }

    /*读取list中数据*/
    public synchronized List<PlayList> loadNewPlayList() {
        while (!loading) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        List<PlayList> playLists = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            PlayList playList = newPlayLists.get(0);
            playLists.add(playList);
            newPlayLists.remove(0);
            if (newPlayLists.size() == 0) {
                loading = false;
                notifyAll();
                break;
            }
        }
        return playLists;
    }

    /*按页获取最新歌单的id*/
    public static List<PlayList> crawlNewPlayList(int pageNum) {
        List<PlayList> playLists = new ArrayList<>();
        Document document = null;
        try {
            document = Jsoup
                    .connect("https://music.163.com//discover/playlist/?order=new&cat=%E5%85%A8%E9%83%A8&limit=35&offset=" + pageNum * 35)
                    .header("Referer", "https://music.163.com")
                    .header("Host", "music.163.com").get();
            Elements elements = document.select("a[class=msk]");
            for (Element element : elements) {
                playLists.add(new PlayList(Long.parseLong(element.attr("href").split("=")[1])));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        return playLists;
    }

    /*按id爬取歌单信息*/
    public static PlayList crawlPlayList(PlayList playList) {
        Document document = null;
        try {
            document = Jsoup.connect("https://music.163.com//playlist?id=" + playList.getId())
                    .header("Referer", "https://music.163.com")
                    .header("Host", "music.163.com")
                    .get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        playList.setName(document.select(".tit h2").text());
        User user = new User();
        user.setId(Long.parseLong(document.select(".name .s-fc7").attr("href").split("=")[1]));
        user.setNickname(document.select(".name .s-fc7").text());
        //playList.setCreator(user);
        return playList;
    }
    /*按id爬取歌曲信息*/
    public static List<Song> crawlSong(PlayList playList) {
        Document document = null;
       
        try {
            document = Jsoup.connect("https://music.163.com//playlist?id=" + playList.getId())
                    .header("Referer", "https://music.163.com")
                    .header("Host", "music.163.com")
                    .get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Elements elements = document.select("ul[class=f-hide] li a");
        List<Song> songList = new ArrayList<>();
        for (Element element : elements) {
        	Song song = new Song();
            song.setId(Long.parseLong(element.attr("href").split("=")[1]));
            song.setName(element.text());
            songList.add(song);
        }
        return songList;
    }

    /*按id爬取歌曲信息*/
    public static Song crawlSong(Song song) {
        String url = "https://music.163.com//song?id=" + song.getId();
        Document document = null;
        try {
            document = Jsoup.connect(url)
                    .header("Referer", "http://music.163.com")
                    .header("Host", "music.163.com")
                    .get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Elements elements = document.select(".tit em");
        song.setName(elements.text());
        elements = document.select(".des span");
       /* song.setAuthor(elements.last().attr("title"));
        Map<String, Object> map = getComments(song.getId());
        song.setCommentCount((int) map.get("total"));
        song.setLyric(getLyric(song.getId()));*/
        return song;
    }

    /*获取评论
    * 使用阿里的fastjson处理json，当前方法<麻烦>*/
    private static Map<String, Object> getComments(long id) {
        Map<String, Object> comments = new HashMap<>();
        String header = postGetComments(id, 0);
        JSONObject jsonObject = JSONObject.parseObject(header);
        int total = (int) jsonObject.get("total");
        comments.put("total", total);
        int pages = total / 20 + (total % 20 == 0 ? 0 : 1);
        List<Comment> commentList = new ArrayList<>();
        for (int i = 0; i < pages; i++) {
            String json = postGetComments(id, i);
            JSONObject temp = JSONObject.parseObject(json);
            List<JSONObject> list = (List) temp.get("comments");
            for (JSONObject object : list) {
                Comment comment = new Comment();
                User user = new User();
                JSONObject userObject = (JSONObject) object.get("user");
                user.setId(Long.parseLong(userObject.get("userId").toString()));
                user.setNickname((String) userObject.get("nickname"));
                user.setAvatarUrl((String) userObject.get("avatarUrl"));
                comment.setUser(user);
                comment.setCommentId(Long.parseLong(object.get("commentId").toString()));
                comment.setTime((long) object.get("time"));
                comment.setContent((String) object.get("content"));
                JSONArray jsonArray = (JSONArray) object.get("beReplied");
                if (jsonArray.size() != 0) {
                    JSONObject beReplied = (JSONObject) jsonArray.get(0);
                    Reply reply = new Reply();
                    reply.setContent((String) beReplied.get("content"));
                    User repliedUser = new User();
                    repliedUser.setId(Long.parseLong(((JSONObject) beReplied.get("user")).get("userId").toString()));
                    repliedUser.setNickname((String) ((JSONObject) beReplied.get("user")).get("nickname"));
                    repliedUser.setAvatarUrl((String) ((JSONObject) beReplied.get("user")).get("avatarUrl"));
                    reply.setUser(user);
                    comment.setBeReplied(reply);
                }
                commentList.add(comment);
                System.out.println("这里是comment:"+comment);
            }
            comments.put("comments", commentList);
            System.out.println("这里是commentList:"+commentList);
        }
        return comments;
    }

    /*发送获取评论的post请求*/
    private static String postGetComments(long id, int pageNum) {
        String firstParam = FIRST_PARAM.replaceAll("offsetNum", pageNum * 20 + "");
        String params = AES(AES(firstParam, FIRTH_PARAM), "FFFFFFFFFFFFFFFF");
        try {
            Connection.Response response = Jsoup.connect("https://music.163.com/weapi/v1/resource/comments/R_SO_4_" + id + "/?csrf_token=")
                    .data(ImmutableMap.of("params", params, "encSecKey", ENCSECKEY))
                    .method(Connection.Method.POST).execute();
            return response.body();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    /*获取歌词*/
    private static String getLyric(long id) {
        String params = "FRz0vQ9xlSySOhxzPbe7klGejaxQzanROzO/uxtUSZjtGTyQUWuzIhKkx3h/+zqs3Wq2Sgy4xdWku4MwkeCKsoZqDQJZQBAh2c5A0VLUCIrkh2drpIR+TdyiFRWF194htU9AmFWgE8VnguUHcb+1kg==";
        String url = "https://music.163.com//song?id=" + id;
        Connection.Response response = null;
        try {
            response = Jsoup.connect("http://music.163.com/api/song/lyric?id=" + id + "&lv=1&kv=1&tv=-1")
                    .header("Referer", url)
                    .header("Host", "https://music.163.com")
                    .data(ImmutableMap.of("params", params, "encSecKey", ENCSECKEY))
                    .method(Connection.Method.POST)
                    .execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
        JSONObject jsonObject = JSONObject.parseObject(response.body());
        Map<String, Object> lrc = (Map) jsonObject.get("lrc");
        String lyric = (String) lrc.get("lyric");
        return lyric;
    }

    /*AES加密*/
    private static String AES(String str, String key) {
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] raw = key.getBytes();
            SecretKeySpec secretKeySpec = new SecretKeySpec(raw, "AES");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            byte[] encrypted = cipher.doFinal(str.getBytes("UTF-8"));
            return new BASE64Encoder().encode(encrypted);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }
    public static void main(String[] args) {
        Crawler.getComments(4872576);
    }
    
}
